/*
 * $LastChangedDate: 2007-06-21 12:35:04 +0200 (Thu, 21 Jun 2007) $
 * $LastChangedRevision: 200 $
 * $LastChangedBy: david $
 */

import java.io.*;
import java.net.*;


public class FileMngr extends Thread
{
	private File file;
	private FileLock lock;
	private Socket clientSocket;
	private RandomAccessFile raf;
	private static int numFileMngr = 0;
	private String fmode;
	private int id;

	private class InvalidModeException extends Exception
	{
		private InvalidModeException()
		{
			/* nothing */
		}
	}


	/**
	 * Crea un subproceso gestor para el fichero que se encuentra en 'path'.
	 */
	public FileMngr(File file, FileLock lock, Socket socket)
	{
		this.file = file;
		this.lock = lock;
		this.clientSocket = socket;
		id = numFileMngr++;
		Log.println("creado " + this + " para el fichero " + file, Log.INIT);
	}

	/**
	 * Abre el fichero en el modo indicado por 'mode'.
	 */
	public void open(String mode) throws FileNotFoundException, InvalidModeException
	{
		if (!(mode.equals("r") || mode.equals("rw"))) {
			throw new InvalidModeException();
		}
		raf = new RandomAccessFile(file, mode);
		Log.println("abierto fichero " + file + " en modo " + mode, Log.IO);
	}

	/**
	 * Lee un byte de datos del fichero.
	 */
	public String read() throws IOException, InterruptedException
	{
		int output;

		lock.readLock();
		Thread.sleep(200); /* Para aumentar la probabilidad de que haya
				      accesos concurrentes. */
		output = raf.read();
		lock.unlock();
		return new Character((char) output).toString();
	}

	/**
	 * Escribe el string indicado al fichero.
	 */
	public void write(String s) throws IOException, InterruptedException
	{
		lock.writeLock();
		Thread.sleep(200); /* Para aumentar la probabilidad de que haya
				      accesos concurrentes. */
		raf.writeChars(s);
		lock.unlock();
	}

	/**
	 * Cierra el fichero.
	 */
	public void close() throws IOException
	{
		raf.close();
		Log.println("cerrado fichero " + file, Log.IO);
		clientSocket.close();
		Log.println("socket " + clientSocket + " cerrado", Log.CONNECTION);
	}

	/**
	 * Sirve a la petición indicada y devuelve una respuesta correspondiente.
	 *
	 * @param petition El formato de la petición en caso de una petición de
	 * lectura es:
	 * 	READ:<SEEK>
	 * donde <SEEK> es un número que indica el seek del cual se debe leer
	 * del fichero. En caso de una escritura el formato es:
	 * 	WRITE:<SEEK>:<DATA>
	 * donde <SEEK> indica el seek al cual escribir en el fichero y <DATA>
	 * indica los datos a escribir.
	 */
	private String servePetition(String petition) throws
		IOException, InterruptedException
	{
		String answer;

		Log.println("Recibido petición " + petition, Log.CONNECTION);
		if (petition.equals("CLOSE")) {
			answer = "ACK CLOSE";
		} else {
			String[] cmd = petition.split(":");

			raf.seek(Long.parseLong(cmd[1]));
			if (cmd[0].equals("READ")) {
				answer = read();
			} else if (cmd[0].equals("WRITE")) {
				write(cmd[2]);
				answer = "ESCRITO " + cmd[2];
			} else {
				answer = "ERROR: COMANDO DESCONOCIDO";
			}
		}
		return answer;
	}

	public void run()
	{
		PrintWriter out = null;
		BufferedReader in = null;
		try {
			try {
				out = new
				PrintWriter(clientSocket.getOutputStream(), true);

				in = new BufferedReader(new
				InputStreamReader(clientSocket.getInputStream()));
			} catch (IOException e) {
				System.exit(1);
			}
			fmode = in.readLine();
			open(fmode);
			out.println("FICHERO ABIERTO");

			String input, output;

			while ((input = in.readLine()) != null) {
				output = servePetition(input);
				out.println(output);
				if (output.equals("ACK CLOSE") ||
					output.equals("ERROR: COMANDO " +
							"DESCONOCIDO"))
				{
					break;
				}
			}
			close();
		} catch (InvalidModeException e) {
			out.println("ERROR: MODO NO VALIDO");
		} catch (FileNotFoundException e) {
			out.println("ERROR: FICHERO NO EXISTE");
		} catch (IOException e) {
			out.println("ERROR: PROBLEMA DE E/S");
		} catch (InterruptedException e) {
			out.println("ERROR");
		}
	}

	public String toString()
	{
		return "(FileMngr" + id + ", " + file + ", " + fmode + ")";
	}
}
